 using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Forms;
using System.IO;

namespace ConnectionAnalyzer
{
    enum AuthenticationReturn
    {
        Success,
        BadLogin,
        TimeOut,
        CorruptAuthKey,
        Unknown
    }
    class Authentication
    {
        private CookieManager cookieManager;

        private static string appId = "624687550958512";
        private static string appSecret = "ff23b9fb92bdf3c87f4f204d6ff007ac";

        private static string publishPermissions = "publish_stream,offline_access,";
        private static string dataPermissionsUser = "email,read_stream,read_mailbox,xmpp_login,user_about_me,user_activities,user_birthday,user_education_history,user_events,user_groups,user_hometown,user_interests,user_likes,user_location,user_notes,user_online_presence,user_photo_video_tags,user_photos,user_relationships,user_religion_politics,user_status,user_website,user_work_history,read_friendlists,read_requests,";
        private static string dataPermissionsFriends = "friends_about_me,friends_activities,friends_birthday,friends_education_history,friends_events,friends_groups,friends_hometown,friends_interests,friends_likes,friends_location,friends_notes,friends_online_presence,friends_photo_video_tags,friends_photos,friends_relationships,friends_religion_politics,friends_status,friends_website,friends_work_history";
        private static string permissions = "&scope=" + publishPermissions + dataPermissionsUser + dataPermissionsFriends;
        private static string redirectUrl = @"http://www." + Settings.Domain + ".com/connect/login_success.html";
        private static string authURL = @"https://graph." + Settings.Domain + ".com/oauth/authorize?client_id=" + appId + "&redirect_uri=" + redirectUrl + permissions + "&display=wap";
        private static string authKeyURL = @"https://graph." + Settings.Domain + ".com/oauth/access_token?client_id=" + appId + "&redirect_uri=" + redirectUrl + "&client_secret=" + appSecret + "&code=";

        private TextBox _output;

        /// <summary>
        /// Gets or sets the name of the user.
        /// </summary>
        /// <value>The name of the user.</value>
        public string UserName { get; set; }
        /// <summary>
        /// Gets or sets the password.
        /// </summary>
        /// <value>The password.</value>
        public string Password { get; set; }
        /// <summary>
        /// Gets or sets the authentication key.
        /// </summary>
        /// <value>The authentication key.</value>
        public string AuthKey { get; set; }
        /// <summary>
        /// Gets the cookie manager.  Used to store and retrieve browser cookies.
        /// This string is used to save the cookies for the session.
        /// </summary>
        /// <value>The cookie manager.</value>
        public string CookieManager { get { return cookieManager.ToString(); } }

        /// <summary>
        /// Initializes a new instance of the <see cref="Authentication"/> class.
        /// </summary>
        public Authentication()
        {
            cookieManager = new CookieManager();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Authentication"/> class.
        /// This CTOR loads the CookieManager with the proper key/values to automatically
        /// log into facebook
        /// </summary>
        /// <param name="cookies">A string representation of a CookieManager object.</param>
        public Authentication(string auth_key)
        {
            AuthKey = auth_key;
            cookieManager = new CookieManager();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Authentication"/> class.
        /// takes a user name and password and authenticates with them
        /// </summary>
        /// <param name="userName">Name of the user.</param>
        /// <param name="pass">The password.</param>
        public Authentication(string userName, string pass, TextBox output)
        {
            _output = output;
            UserName = userName;
            Password = pass;
            cookieManager = new CookieManager();
        }

        /// <summary>
        /// Tests for a successful login.
        /// </summary>
        /// <returns>true if the user has given the proper keys to login</returns>
        public AuthenticationReturn TestForSuccess()
        {
            AuthenticationReturn ret = AuthenticationReturn.Unknown;
            try
            {
                if (!string.IsNullOrEmpty(AuthKey))
                {
                    var text = WebDownloader.DownloadJSON("https://graph." + Settings.Domain + ".com/me?" + AuthKey);

                    if (string.IsNullOrEmpty(text))
                        return AuthenticationReturn.CorruptAuthKey;

                    return AuthenticationReturn.Success;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            return ret;
        }

        /// <summary>
        /// Authenticates this instance.
        /// </summary>
        /// <returns></returns>
        public AuthenticationReturn Authenticate()
        {
            string[] strs = getLoginStrings();

            if (strs == null)
                return AuthenticationReturn.TimeOut;

            string loginUrl = strs[0];
            string vars = strs[1];

            string html = WebDownloader.PostData(loginUrl, vars, cookieManager);
            mark("Authenticate 1", html);
            
            if (!html.Contains("<title>Redirecting...</title>"))
            {
                return AuthenticationReturn.BadLogin;
            }

            return getAuthKey();
        }

        /// <summary>
        /// Gets the login strings.  This is two strings that represents the authentication URL
        /// and the vars to be posted to Facebook
        /// </summary>
        /// <returns></returns>
        private string[] getLoginStrings()
        {
            string html = WebDownloader.DownloadHTML(authURL, cookieManager);
            mark("getLoginString 1", html);
            if (string.IsNullOrEmpty(html))
                return null;

            string loginUrl = "";
            string vars = "";

            Regex urlReg = new Regex(@"<form.*?action=""(?<url>.*?)""");
            Regex reg = new Regex(@"<input(?<body>.*?)/>", RegexOptions.Multiline);
            Regex bodyId = new Regex(@"name=""(?<id>.*?)""");
            Regex bodyVal = new Regex(@"value=""(?<value>.*?)""");
            Regex limit = new Regex(@"<form.*?>(?<body>.*?)</form>", RegexOptions.Multiline);

            loginUrl = urlReg.Match(html).Groups["url"].Value;
            if (!loginUrl.StartsWith("http"))
            {
                loginUrl = "https://login." + Settings.Domain + ".com" + loginUrl;
            }
            html = limit.Match(html).Groups["body"].Value;

            var match = reg.Matches(html);

            foreach (var m in match.Cast<Match>())
            {
                var mbId = bodyId.Match(m.Groups["body"].Value).Groups["id"].Value;
                var mbVal = bodyVal.Match(m.Groups["body"].Value).Groups["value"].Value;

                if (!String.IsNullOrEmpty(vars))
                    vars += "&";
                vars += mbId;

                if (mbId == "email")
                    vars += "=" + UserName;
                else if (mbId == "pass")
                    vars += "=" + Password;
                else
                    vars += "=" + mbVal;
            }

            return new string[] { loginUrl, vars };
        }

        private void mark(string p)
        {
            _output.Text += "================\n" + p + "================\n\n";
            Application.DoEvents();
        }

        private void mark(string p, string p2)
        {
            _output.Text += "================\n" + p + "================\n\n";
            output(p2);
            Application.DoEvents();
        }

        private void output(string p)
        {
            _output.Text += p;
        }

        /// <summary>
        /// Gets the authentication key.
        /// </summary>
        /// <returns></returns>
        private AuthenticationReturn getAuthKey()
        {
            AuthenticationReturn ret = AuthenticationReturn.Unknown;
            string html = "";
            string url = "";

            try
            {
                var response = WebDownloader.GetRawResponse(authURL, cookieManager);

                if (response == null)
                    return AuthenticationReturn.TimeOut;

                cookieManager.StoreCookies(response);

                if (response.ResponseUri.AbsoluteUri.Contains(redirectUrl))
                {
                    mark("getAuthKey: Success! (1)");
                    AuthKey = getAuthKey(response.ResponseUri.Query.Remove(0, 6));
                    ret = AuthenticationReturn.Success;
                }
                else
                {
                    mark("getAuthKey", response.ResponseUri.AbsoluteUri);

                    Stream dataStream = response.GetResponseStream();
                    StreamReader reader = new StreamReader(dataStream);

                    html = reader.ReadToEnd();
                    url = response.ResponseUri.LocalPath;
                    reader.Close();
                    dataStream.Close();


                    mark("getAuthKey HTML", html);
                    mark("getAuthKey URL", url);

                    string tempUrl = getHTMLRedirectUrl(cookieManager, html);
                    if (tempUrl.Contains(redirectUrl))
                    {
                        mark("getAuthKey: Success! (2)");
                        var uri = new Uri(tempUrl);
                        AuthKey = getAuthKey(uri.Query.Remove(0, 6));
                        ret = AuthenticationReturn.Success;
                    }
                }
                response.Close();
            }
            catch (Exception ex)
            {
                mark("getAuthKey Error", ex.Message);
                mark("getAuthKey Error Stack Trace", ex.StackTrace);
                //saveDebugInfo(ex.Message + "\n" + ex.StackTrace, "ERROR-getAuthKey");
            }

            if (ret == AuthenticationReturn.Success)
                return ret;

            if (url == @"/connect/uiserver.php")
            {
                mark("You Need to Authorize XDAFacebook first!", url);
            }

            //saveDebugInfo("", "AuthenticationReturn-Unknown1");
            return AuthenticationReturn.Unknown;
        }

        private string getHTMLRedirectUrl(CookieManager cookieManager, string html)
        {
            if (html.Contains("window.location.href"))
            {

                Regex reg = new Regex(@"window\.location\.href=\""(?<uri>.*?)\""\;", RegexOptions.IgnoreCase | RegexOptions.Multiline);
                if (reg.IsMatch(html))
                {
                    string newUri = "";
                    newUri = reg.Match(html).Groups["uri"].Value.Replace("\\", "");
                    if (!string.IsNullOrEmpty(newUri))
                    {
                        var response = WebDownloader.GetRawResponse(newUri, cookieManager);

                        if (response == null)
                            return null;

                        cookieManager.StoreCookies(response);

                        Stream dataStream = response.GetResponseStream();
                        StreamReader reader = new StreamReader(dataStream);

                        string ret = reader.ReadToEnd();
                        reader.Close();
                        dataStream.Close();

                        mark("getAuthKey getHTMLRedirectUrl", ret);

                        return response.ResponseUri.AbsoluteUri;
                    }
                }
            }
            return "";
        }

        private string getAuthKey(string code)
        {
            return WebDownloader.DownloadHTML(authKeyURL + code, cookieManager);
        }
    }
}
